#include "auth_login.h"

#include <core/QbLogger.h>
#include <core/sharedata/login_data.h>
#include <qbkernel/include/qb_base.h>
#include <qbmessage/include/message_kernel.h>

#include "../ServerInfo.h"
#include "auth_login.inl"

#ifndef _MSC_VER
#include <arpa/inet.h>
#endif
#include <QRegExp>

#include "../auth_mac.h"

namespace qb {
int CAuthLogin::sReloginTime[] = {10,   10,   10,   20,   20,   20,
                                  60,   60,   60,   300,  300,  300,
                                  1800, 1800, 1800, 3600, 3600, 3600};

CAuthLogin& CAuthLogin::instance() {
  static CAuthLogin slogin;
  return slogin;
}

CAuthLogin::CAuthLogin() : m_expired(true), m_try_to_expire(false) {
  m_TaskGroup.SetObserver(this);
  qbBase::getMessageSync()->RegisterMessageObserverTUS(this);
}

CAuthLogin::~CAuthLogin() {
  // destory();
}

int32_t CAuthLogin::destory() {
  // 删除登录任务
  m_TaskGroup.StopLoginSteps();

  // 清理登录任务
  m_TaskGroup.DeleteTaskGroups();

  qbBase::getMessageSync()->DeRegisterMessageObserverTUS(this);
  qbBase::getNetworkService()->DeRegisterNetworkServiceObserver(
      loginData::instance().ConnectID(), this);
  // 断开当前的连接
  CloseConnect();

  stopReLoginTimer();

  return 0;
}

int32_t CAuthLogin::login() {
  if (isLogined()) {
    log_error("already loggedin");
    assert(0);
    return -1;
  }

  if (isLoginning()) {
    log_error("already loginning");
    assert(0);
    return -1;
  }

  LoginUserInfo lui = QuoteServerInfo::instance().userInfo();
  QString strMac = getHostMacAddress();
  strMac = strMac.replace(QRegExp("\\:"), "-");
  // 更新登录用户数据
  loginData::instance().UserInfo().strAccount = lui.strUser;
  loginData::instance().UserInfo().strRawPassword = lui.strPwd;
  loginData::instance().UserInfo().strMac = strMac.toStdString();

  ServerInfo si = QuoteServerInfo::instance().currentServer();
  loginData::instance().SetServerLoginAddress(si.address, si.port);
  // 重置状态数据
  loginData::instance().SetCurrentStatus(eNetStatus_None);
  loginData::instance().ResetLoginData();
  loginData::instance().SetReLogin(false);

  return m_TaskGroup.StartLoginSteps();
}

int32_t CAuthLogin::reLogin() {
  if (isLoginning()) {
    return -1;
  }

  // 停止当前的登录任务
  loginData::instance().SetCurrentStatus(eNetStatus_None);
  loginData::instance().ResetLoginData();
  loginData::instance().SetReLogin(true);

  m_TaskGroup.StopLoginSteps();

  // 断开当前的连接
  CloseConnect();

  // 开始登录任务
  qbBase::getMessageSync()->DeRegisterMessageObserverTUS(this);
  qbBase::getMessageSync()->RegisterMessageObserverTUS(this);
  return m_TaskGroup.StartLoginSteps();
}

int32_t CAuthLogin::manualReLogin() {
  stopReLoginTimer();
  startReLoginTimer(std::bind(&CAuthLogin::onTimer, this));
  return reLogin();
}

bool CAuthLogin::isLoginning() { return loginData::instance().IsLoginning(); }

bool CAuthLogin::isLogined() { return loginData::instance().IsLoggedin(); }

bool CAuthLogin::isReLogin() { return loginData::instance().IsReLogin(); }

int32_t CAuthLogin::logout() {
  QB_CHECK_RETURN_VALUE(isLogined() || isLoginning(), -1);

  if (isLoginning()) {
    // 断开当前的连接
    CloseConnect();

    // 停止当前的登录任务
    loginData::instance().SetCurrentStatus(eNetStatus_None);
    loginData::instance().ResetLoginData();

    m_TaskGroup.StopLoginSteps();
  } else {
    loginData::instance().SetCurrentStatus(eNetStatus_None);
    loginData::instance().ResetLoginData();
  }

  return 0;
}

STDMETHODIMP_(void) CAuthLogin::OnNetData(char* pRecvData, int nDataLen) {
  OnRecvMsg(pRecvData, nDataLen);
}

STDMETHODIMP_(void) CAuthLogin::OnNetStatus(int status) {
  OnRecvNetStauts(status);
}

STDMETHODIMP_(void)
CAuthLogin::OnFailed(THIS_ int32_t connect_id, int32_t error_id) {
  QB_CHECK_RETURN_VOID1(connect_id == loginData::instance().ConnectID());

  if (isLoginning()) {
    return;
  }

  qbBase::getNetworkService()->NotifyDisconnect(connect_id);

  manualReLogin();
}

void CAuthLogin::CloseConnect() {
  qbBase::getMessageKernel()->CloseConnect(loginData::instance().ConnectID());
}

void CAuthLogin::OnTaskStep(qb::NetStatus uStep, short ret_code) {
  if (eNetStatus_ConnectingLogin == uStep || eNetStatus_ConnectingGW == uStep) {
    qbBase::getMessageKernel()->CloseConnect(loginData::instance().ConnectID());
    loginData::instance().ConnectID() =
        qbBase::getMessageKernel()->CreateConnect();
  }

  qbBase::getMessageKernel()->NotifyNetStatus(uStep, ret_code);
}

void CAuthLogin::OnAllTaskFinished() {
  stopReLoginTimer();

  qbBase::getMessageSync()->DeRegisterMessageObserverTUS(this);
  qbBase::getNetworkService()->RegisterNetworkServiceObserver(
      loginData::instance().ConnectID(), this);
  qbBase::getMessageKernel()->NotifyNetStatus(eNetStatus_Done, 0);

  loginData::instance().SetCurrentStatus(eNetStatus_Done);

  if (loginData::instance().IsReLogin()) {
    qbBase::getNetworkService()->NotifyRecover(
        loginData::instance().ConnectID());
  }
}

void CAuthLogin::OnTaskGroupFailed(qb::NetStatus uStep, short ret_code) {
  switch (ret_code) {
    case eNetError_ReloginServer: {
      // 断开当前的网络连接
      CloseConnect();

      // 异步删除登录任务组对象
      m_TaskGroup.StopLoginSteps();

      // 重新开始登录任务
      m_TaskGroup.StartLoginSteps();
    } break;
    default: {
      // 断开当前的网络连接
      CloseConnect();

      // 重置状态数据
      loginData::instance().SetCurrentStatus(eNetStatus_None);
      loginData::instance().ResetLoginData();

      // 异步删除登录任务组对象
      m_TaskGroup.StopLoginSteps();

      log_error("login step:%d, errorcode:%d", uStep, ret_code);

      qbBase::getMessageKernel()->NotifyNetStatus(uStep, ret_code);
    } break;
  }
}

void CAuthLogin::startReLoginTimer(std::function<void()> task) {
  if (m_expired == false) return;

  m_expired = false;
  m_try_to_expire = false;

  std::thread([this, task]() mutable {
    int times = 0;
    int curTimeout = 0;
    int count = sizeof(sReloginTime) / sizeof(int);
    while (!m_try_to_expire) {
      if (curTimeout >= count) {
        break;
      }
#ifdef WIN32
      ::Sleep(100);  // avoid sleep_until implementation bug.
#else
      std::this_thread::sleep_for(std::chrono::milliseconds(100));
#endif
      times++;
      if (times >= sReloginTime[curTimeout] * 10) {
        curTimeout++;
        times = 0;
        task();
      }
    }
    {
      std::lock_guard<std::mutex> locker(m_mutex);
      m_expired = true;
      m_expired_cond.notify_one();
    }
  }).detach();
}

void CAuthLogin::stopReLoginTimer() {
  if (m_expired || m_try_to_expire) return;

  m_try_to_expire = true;
  {
    std::unique_lock<std::mutex> locker(m_mutex);
    m_expired_cond.wait(locker, [this] { return m_expired == true; });
    m_try_to_expire = false;
  }
}

void CAuthLogin::onTimer() { reLogin(); }
}  // namespace qb
